home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / OTStreamLogViewer / LogEngine / LogEngine.c next >
Encoding:
C/C++ Source or Header  |  2000-09-28  |  11.8 KB  |  388 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        LogEngine.c
  3.  
  4.     Contains:    The core code to talk to the STREAMS log module.
  5.  
  6.     Written by: Quinn "The Eskimo!"    
  7.  
  8.     Copyright:    Copyright © 1998-1999 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.                 7/23/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  20.                 
  21.  
  22. */
  23.  
  24. #define qDebug 1
  25.  
  26. /////////////////////////////////////////////////////////////////////
  27. // Pick up the standard C stuff.
  28. #include <Processes.h>
  29. #include <stdio.h>
  30. #include <string.h>
  31.  
  32. /////////////////////////////////////////////////////////////////////
  33. // Pick up low-level OT APIs.
  34.  
  35. #include <OTDebug.h>
  36. #include <stropts.h>
  37. #include <mistream.h>
  38.  
  39. /////////////////////////////////////////////////////////////////////
  40. // Pick up the symbolic name of the various OT modules.
  41.  
  42. #include <modnames.h>
  43.  
  44. /////////////////////////////////////////////////////////////////////
  45. // OTDebugStr is not defined in any OT header files, but it is
  46. // exported by the libraries, so we define the prototype here.
  47.  
  48. extern pascal void OTDebugStr(const char* str);
  49.  
  50. /////////////////////////////////////////////////////////////////////
  51. // Pick up our types and prototypes.
  52.  
  53. #include "LogEngine.h"
  54.  
  55. /////////////////////////////////////////////////////////////////////
  56.  
  57. // If we can't log an entry (because of lack of memory), we
  58. // increment gDroppedLogEntries.  The client can access this to
  59. // tell the user that we're dropped logs.
  60.  
  61. static UInt32 gDroppedLogEntries = 0;
  62.  
  63. // gNewLogEntries is a FIFO containing log entries that have
  64. // been read but not yet delivered to the client.  It is populated
  65. // at interrupt time by our notifier (LogNotifier) and consumed
  66. // at system task time by ForEachNewLogEntry.
  67.  
  68. static OTLIFO gNewLogEntries = { nil };
  69.  
  70. static SInt32 gNumberInList = 0;
  71.  
  72. // We record the PSN of the current process when we start up
  73. // so that we can wake ourselves up when log entries arrive.
  74. // We also have a flag to say whether we've already called
  75. // WakeUpProcess, so we don't call it zillions of times.
  76.  
  77. static ProcessSerialNumber gOurProcess;
  78. static OTLock gProcessWoken;
  79.  
  80. /////////////////////////////////////////////////////////////////////
  81.  
  82. static void CreateNewLogEntry(struct log_ctl *logHeader, OTBuffer *logTextBuffer)
  83.     // Create a new LogEntry, fill out the field fields based on
  84.     // logHeader and the variable length data based on the text in
  85.     // logTextBuffer, and add it to the gNewEntries list of recent
  86.     // additions.
  87.     //
  88.     // Context: OT deferred task
  89. {
  90.     OSStatus junk;
  91.     OTBuffer *thisTextBuffer;
  92.     UInt32 textLength;
  93.     LogEntryPtr newEntry;
  94.     UInt8 *destCursor;
  95.  
  96.     // First calculate the number of bytes of text that are in
  97.     // this entry (ie in the logTextBuffer and any buffers chained
  98.     // off it).
  99.     
  100.     textLength = 0;
  101.     thisTextBuffer = logTextBuffer;
  102.     while ( thisTextBuffer != nil ) {
  103.         OTAssert("CreateNewLogEntry: Non-data message in buffer chain", thisTextBuffer->fType == M_DATA);
  104.         textLength += thisTextBuffer->fLen;
  105.         thisTextBuffer = thisTextBuffer->fNext;
  106.     }
  107.  
  108.     // Then allocate a new log entry of the correct size,
  109.     // ie the length of the fixed part + the length of the text.
  110.     
  111.     newEntry = (LogEntryPtr) OTAllocMem( sizeof(LogEntry) + textLength );
  112.     if (newEntry == nil) {
  113.         if (gDroppedLogEntries == 0) {
  114.             // OTDebugStr("CreateNewLogEntry: Started dropping log entries");
  115.         }
  116.         OTAtomicAdd32(1, (SInt32 *) &gDroppedLogEntries);
  117.     } else {
  118.         
  119.         // Fill out the fixed fields at the beginning of the
  120.         // new log entry.
  121.         
  122.         newEntry->fNext = nil;
  123.         newEntry->fMagic = kMagicValue;
  124.         newEntry->fRefCount = 1;
  125.         newEntry->fTextLength = textLength;
  126.         newEntry->fLogHeader = *logHeader;
  127.         
  128.         // Now copy bytes out of the text messages into the
  129.         // variable part of the log entry.  destCursor is the
  130.         // byte we're going to blat next, ie it starts just
  131.         // after the fixed fields of the new log entry and marches 
  132.         // onwards through the variable text data.
  133.         
  134.         destCursor = ((UInt8 *) newEntry) + sizeof(LogEntry);
  135.         
  136.         thisTextBuffer = logTextBuffer;
  137.         while ( thisTextBuffer != nil ) {
  138.             BlockMoveData(thisTextBuffer->fData, destCursor, thisTextBuffer->fLen);
  139.             destCursor += thisTextBuffer->fLen;
  140.             thisTextBuffer = thisTextBuffer->fNext;
  141.         }
  142.         OTAssert("CreateNewLogEntry: Did not copy the number of bytes that we calculated were in the message",
  143.                     destCursor = ((UInt8 *) newEntry) + sizeof(LogEntry) + textLength);
  144.         
  145.         // Finally add the new entry to the global list
  146.         // of new entries to be processed at SystemTask time.
  147.         
  148.         OTLIFOEnqueue(&gNewLogEntries, (OTLink *) &newEntry->fNext);
  149.         
  150.         OTAtomicAdd32(1, &gNumberInList);
  151.         
  152.         // If we haven't already woken up our process, do so now.
  153.         
  154.         if ( OTAcquireLock(&gProcessWoken) ) {
  155.             // OTDebugStr("CreateNewLogEntry: Waking up process");
  156.  
  157.             junk = WakeUpProcess(&gOurProcess);
  158.             OTAssert("CreateNewLogEntry: Error waking up our process", junk == noErr);
  159.         }
  160.     }
  161. }
  162.  
  163. extern pascal void RetainLogEntry(LogEntryPtr thisEntry)
  164.     // See comment in interface part.
  165. {
  166.     OTAssert("RetainLogEntry: Bad magic", thisEntry->fMagic == kMagicValue);
  167.     thisEntry->fRefCount += 1;
  168. }
  169.  
  170. extern pascal void ReleaseLogEntry(LogEntryPtr thisEntry)
  171.     // See comment in interface part.
  172. {
  173.     OTAssert("ReleaseLogEntry: Bad magic", thisEntry->fMagic == kMagicValue);
  174.     thisEntry->fRefCount -= 1;
  175.     if ( thisEntry->fRefCount == 0 ) {
  176.         thisEntry->fMagic = 'bad!';
  177.         OTFreeMem(thisEntry);
  178.     }
  179. }
  180.  
  181. /////////////////////////////////////////////////////////////////////
  182.  
  183. static pascal void LogNotifier(StreamRef strm, OTEventCode code, OTResult err, void *cookie)
  184.     // This is the notifier that OT calls when an event happens on
  185.     // the raw stream.  In this case the only event we're interested in
  186.     // is SIGPOLL, which says that something interesting has happened with
  187.     // data, typically that data has arrived.  We respond to a SIGPOLL
  188.     // by calling OTReadMessage to extract the message from the stream.
  189.     // We then process the message and loop looking for more.
  190.     //
  191.     // OTReadMessage is like getmsg() but it a) operates immediately,
  192.     // so we don't need to wait for a completion event like we do with
  193.     // getmsg, and b) returns the data without copying it.
  194.     //
  195.     // Context: OT deferred task
  196. {
  197.     #pragma unused(err)
  198.     #pragma unused(cookie)
  199.     OTReadInfo readInfo;
  200.     OTBuffer *readBuffer;
  201.  
  202.     // Debugger();
  203.         
  204.     switch (code) {
  205.         case kSIGNALEVENT + SIGPOLL:
  206.             do {
  207.                 readInfo.fType = kOTAnyMsgType;
  208.                 readInfo.fCommand = 0;                // Not strictly necessary because kOTAnyMsgType implies this field is ignored.
  209.                 readBuffer = OTReadMessage(strm, &readInfo);
  210.                 if ( readBuffer != nil ) {
  211.                     if ( (readBuffer->fType == M_PROTO || readBuffer->fType == M_PCPROTO) &&
  212.                              readBuffer->fLen == sizeof(struct log_ctl) ) {
  213.                         CreateNewLogEntry( (struct log_ctl *) readBuffer->fData , readBuffer->fNext);
  214.                     } else {
  215.                         OTDebugStr("LogNotifier: OTReadMessage returned a weird buffer");
  216.                     }
  217.                     OTReleaseBuffer(readBuffer);
  218.                 }
  219.             } while ( readBuffer != nil );
  220.             break;
  221.         default:
  222.             OTDebugStr("LogNotifier: Unexpected event code");
  223.             break;
  224.     }
  225. }
  226.  
  227. /////////////////////////////////////////////////////////////////////
  228.  
  229. static OSStatus SetupLogging(StreamRef strm, int_t command, 
  230.                             struct trace_ids traceInfo[], UInt32 traceInfoCount)
  231.     // Sends a log configuration ioctl to the raw stream.
  232.     // command is the ioctl, traceInfoCount is the number of
  233.     // trace_ids in the buffer pointed to be traceInfo.  Note
  234.     // that traceInfoCount can be zero, a common case for setting
  235.     // up an error log stream.  Note that strm is assumed to be
  236.     // in sync/blocking mode.
  237.     //
  238.     // Context: SystemTask /only/
  239. {
  240.     OSStatus err;
  241.     struct strioctl logIoctl; 
  242.     
  243.     logIoctl.ic_cmd = command;
  244.     logIoctl.ic_timout = -1;
  245.     logIoctl.ic_dp = (char *) traceInfo;
  246.     logIoctl.ic_len = traceInfoCount * sizeof(struct trace_ids);
  247.  
  248.     err = OTStreamIoctl(strm, I_STR, &logIoctl);
  249.     
  250.     return err;
  251. }
  252.  
  253. /////////////////////////////////////////////////////////////////////
  254. // Public Entry Points
  255.  
  256. // This is the raw stream which we use for logging. It is
  257. // set up by StartLogging and torn down by StopLogging.
  258.  
  259. static StreamRef gLogStream = kOTInvalidStreamRef;
  260.  
  261. extern pascal OSStatus StartLogging(Boolean logErrors,
  262.                                 UInt32 traceInfoCount, struct trace_ids traceInfo[])
  263.  
  264.     // See comment in interface part.
  265. {
  266.     OSStatus err;
  267.     OSStatus junk;
  268.  
  269.     OTAssert("StartLogging: paramErr", traceInfoCount == 0 || traceInfo != nil);
  270.     
  271.     OTAssert("StartLogging: We're already logging!", gLogStream == kOTInvalidStreamRef);
  272.  
  273.     OTClearLock(&gProcessWoken);
  274.     
  275.     err = GetCurrentProcess(&gOurProcess);
  276.     
  277.     // Create and configure a stream to the log device.  Note that
  278.     // we do this stuff with the stream in sync/blocking mode, and
  279.     // switch to async/blocking at the end.
  280.  
  281.     if (err == noErr) {
  282.         gLogStream = OTStreamOpen(MI_LOG_DEVICE, 0, &err);
  283.     }
  284.     if (err == noErr) {
  285.         OTStreamSetBlocking(gLogStream);
  286.     }
  287.     if (err == noErr && traceInfoCount > 0) {
  288.     
  289.         // If we've been asked to log traces, tell the log device that
  290.         // we're the trace stream.
  291.         
  292.         err = SetupLogging(gLogStream, I_TRCLOG, traceInfo, traceInfoCount);
  293.     }
  294.     if (err == noErr && logErrors) {
  295.  
  296.         // If we've been asked to log error, tell the log device that
  297.         // we're the error stream.
  298.  
  299.         err = SetupLogging(gLogStream, I_ERRLOG, nil, 0);
  300.     }
  301.     if (err == noErr) {
  302.         
  303.         // We want all signals to arrive in our notifier.  The only one we're
  304.         // specifically interested in is SIGPOLL (ie S_RDNORM etc) but if any
  305.         // other signals are generated we /really/ want to know about it.
  306.         
  307.         err = OTStreamIoctl(gLogStream, I_SETSIG,
  308.                         (void *) (S_INPUT | S_RDNORM | S_RDBAND | S_HIPRI | S_OUTPUT | S_WRNORM | 
  309.                         S_WRBAND | S_MSG | S_ERROR | S_HANGUP | S_BANDURG));
  310.     }
  311.     if (err == noErr) {
  312.         OTStreamSetAsynchronous(gLogStream);
  313.         err = OTStreamInstallNotifier(gLogStream, LogNotifier, gLogStream);
  314.     }
  315.     
  316.     // Clean up.
  317.     
  318.     if (err != noErr) {
  319.         if (gLogStream != kOTInvalidStreamRef) {
  320.             OTStreamRemoveNotifier(gLogStream);
  321.             junk = OTStreamClose(gLogStream);
  322.             OTAssert("StartLogging: OTStreamClose failed", junk == noErr);
  323.         }
  324.         gLogStream = kOTInvalidStreamRef;
  325.     }
  326.     
  327.     return err;
  328. }
  329.  
  330. extern pascal void StopLogging(void)
  331.     // See comment in interface part.
  332. {
  333.     OSStatus junk;
  334.     
  335.     OTAssert("StopLogging: We're not logging!", gLogStream != kOTInvalidStreamRef);
  336.  
  337.     OTStreamRemoveNotifier(gLogStream);
  338.     junk = OTStreamClose(gLogStream);
  339.     OTAssert("StopLogging: OTStreamClose failed", junk == noErr);
  340.     gLogStream = kOTInvalidStreamRef;
  341. }
  342.  
  343. extern pascal Boolean LoggingActive(void)
  344.     // See comment in interface part.
  345. {
  346.     return gLogStream != kOTInvalidStreamRef;
  347. }
  348.  
  349. extern pascal UInt32 NumberOfDroppedLogEntries(void)
  350.     // See comment in interface part.
  351. {
  352.     return gDroppedLogEntries;
  353. }
  354.  
  355. extern pascal void ForEachNewLogEntry(ProcessLogEntryProcPtr doThis, void *refCon)
  356.     // See comment in interface part.
  357. {
  358.     OTLink *thisLink;
  359.     LogEntryPtr thisEntry;
  360.     SInt32 junkLong;
  361.  
  362.     if ( gNumberInList != 0 ) {
  363.         // OTDebugStr("ForEachNewLogEntry: Process woken with something to do");
  364.     }
  365.  
  366.     OTClearLock(&gProcessWoken);
  367.  
  368.     // If you're having troubles understanding this, check out
  369.     // the sample code in the section on lists in "Inside Macintosh:
  370.     // Networking with Open Transport".
  371.         
  372.     do {
  373.         thisLink = OTReverseList(OTLIFOStealList(&gNewLogEntries));
  374.         while ( thisLink != nil ) {
  375.             thisEntry = OTGetLinkObject(thisLink, LogEntry, fNext);
  376.             
  377.             doThis(thisEntry, refCon);
  378.  
  379.             thisLink = thisLink->fNext;
  380.  
  381.             ReleaseLogEntry(thisEntry);
  382.  
  383.             junkLong = OTAtomicAdd32(-1, (SInt32 *) &gNumberInList);
  384.             OTAssert("ForEachNewLogEntry: ", junkLong >= 0);
  385.         }
  386.     } while ( gNewLogEntries.fHead != nil );
  387. }
  388.